----------------------------------------------------------------------------------
-- Company: 
-- Engineer: 
-- 
-- Create Date:    13:25:17 04/16/2012 
-- Design Name: 
-- Module Name:    video_logic_unit - Behavioral 
-- Project Name: 
-- Target Devices: 
-- Tool versions: 
-- Description: 
--
-- Dependencies: 
--
-- Revision: 
-- Revision 0.01 - File Created
-- Additional Comments: 
--
----------------------------------------------------------------------------------
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;

library work;
use work.game_support.ALL;

-- Uncomment the following library declaration if using
-- arithmetic functions with Signed or Unsigned values
use IEEE.NUMERIC_STD.ALL;

-- Uncomment the following library declaration if instantiating
-- any Xilinx primitives in this code.
--library UNISIM;
--use UNISIM.VComponents.all;

entity object_controller is
	Port (
		clk : in std_logic;
		player1_left, player1_right : in std_logic;
		player2_left, player2_right : in std_logic;
		voice : in std_logic;
		top_col, bottom_col : out std_logic;
		h_count : in std_logic_vector(10 downto 0);
		v_count : in std_logic_vector(10 downto 0);
		increment_p1, increment_p2 : out std_logic;
		sw : in std_logic_vector(3 downto 0);
		player1_here, player2_here, ball_here : out std_logic;
		brick_here : out std_logic
	);
end object_controller;

architecture Behavioral of object_controller is
	
	------------------------------------------
	-- Paddle Position Signals ---------------
	------------------------------------------
	
	signal current_paddle1_x, next_paddle1_x : x_position := PADDLE_X_INIT;
	signal current_paddle2_x, next_paddle2_x : x_position := PADDLE_X_INIT;
	
	signal paddle1 : position;
	signal paddle2 : position;
	
	signal player2_direction : std_logic_vector(1 downto 0);
	
	-------------------------------------------
	-- Ball Position Signal ------------------
	-------------------------------------------
	signal current_ball_position, next_ball_position : position := BALL_INIT;
	signal current_ball_direction, next_ball_direction : ball_direction := northwest;
	signal speed : natural range 0 to 1000;
	signal collision : collision_type;
	
	signal last_collision : collision_type;
	
	------------------------------------------
	-- Screen Update Pulse -------------------
	------------------------------------------
	
	signal screen_update_pulse : std_logic;
	
	
	
	
	component VGA_clock is
		generic(pixels_per_sec: natural:=100); 
		port(	
			clock:		in std_logic;
			sw: in std_logic_vector(3 downto 0);
			pulse: 		out std_logic
		);
	end component;
	
	component boundary_detector is
		Port (
			ball, paddle1, paddle2 : in position;
			collision : out collision_type;
			p1_inc, p2_inc : out std_logic
		);
	end component;
	
	component brick_controller is
		Generic (brick_position : in position);
		Port (
			clk : in std_logic;
			ball_position : in position;
			h_count : in std_logic_vector(10 downto 0);
			v_count : in std_logic_vector(10 downto 0);
			here, hit : out std_logic
		);
	end component;
	
	signal brick1_here, brick2_here, brick3_here, brick4_here, brick5_here,
		brick6_here, brick7_here: std_logic;
		
	signal brick1_hit, brick2_hit, brick3_hit, brick4_hit, brick5_hit,
		brick6_hit, brick7_hit: std_logic;
		
	signal brick_hit : std_logic;
	

begin

	brick_here <= brick1_here or brick2_here or brick3_here or brick4_here or brick5_here
		or brick6_here or brick7_here;
		
	brick_hit <= brick1_hit or brick2_hit or brick3_hit or brick4_hit or brick5_hit
		or brick6_hit or brick7_hit;

	brick1 : brick_controller
		generic map (
			brick_position => (x =>SCREEN_WIDTH/2 - BRICK_BOUNDS.width/2,
				y => SCREEN_HEIGHT/2 - BRICK_BOUNDS.height/2))
		port map (
			clk => clk, hit => brick1_hit,
			ball_position => current_ball_position,
			h_count => h_count,
			v_count => v_count,
			here => brick1_here
		);
		
	brick2 : brick_controller
		generic map (
			brick_position => (x => 100,
				y => SCREEN_HEIGHT/2 - BRICK_BOUNDS.height/2))
		port map (
			clk => clk, hit => brick2_hit,
			ball_position => current_ball_position,
			h_count => h_count,
			v_count => v_count,
			here => brick2_here
		);
		
	brick3 : brick_controller
		generic map (
			brick_position => (x => 650,
				y => SCREEN_HEIGHT/2 - BRICK_BOUNDS.height/2))
		port map (
			clk => clk, hit => brick3_hit,
			ball_position => current_ball_position,
			h_count => h_count,
			v_count => v_count,
			here => brick3_here
		);
		
	brick4 : brick_controller
		generic map (
			brick_position => (x => 225,
				y => 200))
		port map (
			clk => clk, hit => brick4_hit,
			ball_position => current_ball_position,
			h_count => h_count,
			v_count => v_count,
			here => brick4_here
		);
		
	brick5 : brick_controller
		generic map (
			brick_position => (x => 525,
				y => 200))
		port map (
			clk => clk, hit => brick5_hit,
			ball_position => current_ball_position,
			h_count => h_count,
			v_count => v_count,
			here => brick5_here
		);
		
	brick6 : brick_controller
		generic map (
			brick_position => (x => 225,
				y => 400))
		port map (
			clk => clk, hit => brick6_hit,
			ball_position => current_ball_position,
			h_count => h_count,
			v_count => v_count,
			here => brick6_here
		);
		
	brick7 : brick_controller
		generic map (
			brick_position => (x => 525,
				y => 400))
		port map (
			clk => clk, hit => brick7_hit,
			ball_position => current_ball_position,
			h_count => h_count,
			v_count => v_count,
			here => brick7_here
		);
		

	player2_direction <= player2_left & player2_right;

	paddle1.x <= current_paddle1_x;
	paddle1.y <= PADDLE1_Y;
	
	paddle2.x <= current_paddle2_x;
	paddle2.y <= PADDLE2_Y;
	
	update_pulse_generator : VGA_clock
		generic map (pixels_per_sec => 100)
		port map (
			clock => clk,
			sw => sw,
			pulse => screen_update_pulse
		);
		
	my_boundary_detector : boundary_detector	Port map(
		ball => current_ball_position,
		paddle1 => paddle1,
		paddle2 => paddle2,
		collision => collision
	);
	
	increment_p1 <= '1' when (last_collision = none and collision = top_collision)
		or (brick_hit = '1' and (current_ball_direction = northwest or current_ball_direction = northeast))
	else '0';
	increment_p2 <= '1' when (last_collision = none and collision = bottom_collision)
		or (brick_hit = '1' and (current_ball_direction = southwest or current_ball_direction = southeast))
	else '0';
		
	-- Data Latch Process --
	process(clk)
	begin
		if rising_edge(clk) then
			current_paddle1_x <= next_paddle1_x;
			current_paddle2_x <= next_paddle2_x;
			current_ball_position <= next_ball_position;
			current_ball_direction <= next_ball_direction;
			last_collision <= collision;
		end if;
	end process;
	

	process(collision)
	begin
		if collision = top_collision then
			top_col <= '1';
		elsif collision = bottom_collision then
			bottom_col <= '1';
		else
			top_col <= '0';
			bottom_col <= '0';
		end if;
	end process;
	-- Next Data Logic --
	process(current_paddle1_x, current_paddle2_x, screen_update_pulse, collision,
		player1_left, player1_right, player2_left, player2_right, current_ball_position,
		current_ball_direction)
	begin
	
		-- Default
		next_paddle1_x <= current_paddle1_x;
		next_paddle2_x <= current_paddle2_x;
		next_ball_position <= current_ball_position;
		
		
		
	
		if screen_update_pulse = '1' then		
			if player1_right = '1' then
				next_paddle1_x <= current_paddle1_x + 1;
			elsif player1_left = '1' then
				next_paddle1_x <= current_paddle1_x - 1;
			else
				next_paddle1_x <= current_paddle1_x;
			end if;
			
		else
			next_paddle1_x <= current_paddle1_x;
		end if;
		
		if screen_update_pulse = '1' then
			case player2_direction is
				when "10" =>
					next_paddle2_x <= current_paddle2_x - 1;
				when "01" =>
					next_paddle2_x <= current_paddle2_x + 1;
				when others =>
					next_paddle2_x <= current_paddle2_x;
			end case;
		else
			next_paddle2_x <= current_paddle2_x;
		end if;
		
		if screen_update_pulse = '1' then
			next_ball_direction <= get_new_direction(current_ball_direction, collision);
			next_ball_position <= get_new_position(current_ball_position, current_ball_direction, collision);
		else
			next_ball_direction <= current_ball_direction;
			next_ball_position <= current_ball_position;
		end if;
	end process;
	--Output Logic Process
	
	process(h_count, v_count, paddle1, paddle2, current_ball_position)
	begin
		if in_paddle_bounds(paddle1, h_count, v_count) then
			player1_here <= '1';
		else
			player1_here <= '0';
		end if;
		
		if in_paddle_bounds(paddle2, h_count, v_count) then
			player2_here <= '1';
		else
			player2_here <= '0';
		end if;
		
		if in_ball_bounds(current_ball_position, h_count, v_count) then
				ball_here <= '1';
		else
				ball_here <= '0';
		end if;
	end process;

end Behavioral;
